#include "stdafx.h"
#include "GTKHost.h"
#include "FCFile.h"


GTKTimer::GTKTimer(){
    m_interval = 1000;
    m_tick = 0;
    m_timerID = 0;
}

GTKTimer::~GTKTimer(){

}

GTKHost::GTKHost(){
    m_native = 0;
    m_allowOperate = true;
    m_allowPartialPaint = false;
    m_touchPoint.x = 0;
    m_touchPoint.y = 0;
    m_clientSize.cx = 0;
    m_clientSize.cy = 0;
    m_invokeSerialID = 0;
    m_drawingArea = 0;
    m_window = 0;
}

GTKHost::~GTKHost(){
    std::map<int, GTKTimer*>::iterator sIter = m_timers.begin();
    for(; sIter != m_timers.end(); ++sIter){
        delete sIter->second;
    }
    m_timers.clear();
    m_native = 0;
    m_drawingArea = 0;
    m_window = 0;
}

FCNative* GTKHost::getNative(){
    return m_native;
}

void GTKHost::setNative(FCNative *value){
    m_native = value;
}

FCSize GTKHost::getClientSize(){
    return m_clientSize;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void GTKHost::activeMirror(){
}

bool GTKHost::allowOperate(){
    return m_allowOperate;
}

bool GTKHost::allowPartialPaint(){
    return m_allowPartialPaint;
}

void GTKHost::beginInvoke(FCView *view, Object args){
    int seraialID = 0;
    m_lock.lock();
    m_invokeSerialID++;
    seraialID = m_invokeSerialID;
    m_invokeArgs[seraialID] = args;
    m_invokeViews[seraialID] = view;
    m_lock.unLock();
}

void GTKHost::copy(lstring text){
}

FCView* GTKHost::createInternalView(FCView *parent, const String& clsid){
    FCSplitLayoutDiv *splitLayoutDiv = dynamic_cast<FCSplitLayoutDiv*>(parent);
    if (splitLayoutDiv){
        if (clsid == L"splitter"){
            FCButton *splitter = new FCButton;
            FCSize size = {5, 5};
            splitter->setSize(size);
            return splitter;
        }
    }
    FCScrollBar *scrollBar = dynamic_cast<FCScrollBar*>(parent);
    if (scrollBar){
        if (clsid == L"addbutton"){
            FCButton *addButton = new FCButton;
            FCSize size = {15, 15};
            addButton->setSize(size);
            return addButton;
        }
        else if (clsid == L"backbutton"){
            return new FCButton();
        }
        else if (clsid == L"scrollbutton"){
            FCButton *scrollButton = new FCButton;
            scrollButton->setAllowDrag(true);
            return scrollButton;
        }
        else if (clsid == L"reducebutton"){
            FCButton *reduceButton = new FCButton;
            FCSize size = {15, 15};
            reduceButton->setSize(size);
            return reduceButton;
        }
    }
    FCTabPage *tabPage = dynamic_cast<FCTabPage*>(parent);
    if (tabPage){
        if (clsid == L"headerbutton"){
            FCButton *button = new FCButton;
            button->setAllowDrag(true);
            FCSize size = {100, 20};
            button->setSize(size);
            return button;
        }
    }
    FCComboBox *comboBox = dynamic_cast<FCComboBox*>(parent);
    if (comboBox){
        if (clsid == L"dropdownbutton"){
            FCButton *dropDownButton = new FCButton;
            dropDownButton->setDisplayOffset(false);
            int width = comboBox->getWidth();
            int height = comboBox->getHeight();
            FCPoint location = {width - 16, 0};
            dropDownButton->setLocation(location);
            FCSize size = {16, height};
            dropDownButton->setSize(size);
            return dropDownButton;
        }
        else if (clsid == L"dropdownmenu"){
            FCComboBoxMenu *comboBoxMenu = new FCComboBoxMenu;
            comboBoxMenu->setComboBox(comboBox);
            comboBoxMenu->setPopup(true);
            FCSize size = {100, 200};
            comboBoxMenu->setSize(size);
            return comboBoxMenu;
        }
    }
    FCDateTimePicker *datePicker = dynamic_cast<FCDateTimePicker*>(parent);
    if (datePicker){
        if (clsid == L"dropdownbutton"){
            FCButton *dropDownButton = new FCButton;
            dropDownButton->setDisplayOffset(false);
            int width = datePicker->getWidth();
            int height = datePicker->getHeight();
            FCPoint location = {width - 16, 0};
            dropDownButton->setLocation(location);
            FCSize size = {16, height};
            dropDownButton->setSize(size);
            return dropDownButton;
        }
        else if (clsid == L"dropdownmenu"){
            FCMenu *dropDownMenu = new FCMenu();
            FCPadding padding(1);
            dropDownMenu->setPadding(padding);
            dropDownMenu->setPopup(true);
            FCSize size = {200, 200};
            dropDownMenu->setSize(size);
            return dropDownMenu;
        }
    }
    FCSpin *spin = dynamic_cast<FCSpin*>(parent);
    if (spin){
        if (clsid == L"downbutton"){
            FCButton *downButton = new FCButton;
            downButton->setText(L"-");
            downButton->setDisplayOffset(false);
            FCSize size = {16, 16};
            downButton->setSize(size);
            return downButton;
        }
        else if (clsid == L"upbutton"){
            FCButton *upButton = new FCButton;
            upButton->setText(L"+");
            upButton->setDisplayOffset(false);
            FCSize size = {16, 16};
            upButton->setSize(size);
            return upButton;
        }
    }
    FCDiv *div = dynamic_cast<FCDiv*>(parent);
    if (div){
        if (clsid == L"hscrollbar"){
            FCHScrollBar *hScrollBar = new FCHScrollBar;
            hScrollBar->setVisible(false);
            FCSize size = {15, 15};
            hScrollBar->setSize(size);
            return hScrollBar;
        }
        else if (clsid == L"vscrollbar"){
            FCVScrollBar *vScrollBar = new FCVScrollBar;
            vScrollBar->setVisible(false);
            FCSize size = {15, 15};
            vScrollBar->setSize(size);
            return vScrollBar;
        }
    }
    FCGrid *grid = dynamic_cast<FCGrid*>(parent);
    if(grid){
        if(clsid == L"edittextbox"){
            return new FCTextBox;
        }
    }
    return 0;
}

FCCursors GTKHost::getCursor(){
    return FCCursors_Arrow;
}

int GTKHost::getIntersectRect(FCRect *lpDestRect, const FCRect *lpSrc1Rect, const FCRect *lpSrc2Rect){
    lpDestRect->left = FCTran::maxValue(lpSrc1Rect->left, lpSrc2Rect->left);
    lpDestRect->right = FCTran::minValue(lpSrc1Rect->right, lpSrc2Rect->right);
    lpDestRect->top = FCTran::maxValue(lpSrc1Rect->top, lpSrc2Rect->top);
    lpDestRect->bottom = FCTran::minValue(lpSrc1Rect->bottom, lpSrc2Rect->bottom);
    if(lpDestRect->right > lpDestRect->left && lpDestRect->bottom > lpDestRect->top){
        return 1;
    }
    else{
        lpDestRect->left = 0;
        lpDestRect->right = 0;
        lpDestRect->top = 0;
        lpDestRect->bottom = 0;
        return 0;
    }
}

FCPoint GTKHost::getTouchPoint(){
    FCPoint mp = m_touchPoint;
    if (m_native->allowScaleSize())
    {
        FCSize clientSize = getClientSize();
        if (clientSize.cx > 0 && clientSize.cy > 0)
        {
            FCSize scaleSize = m_native->getScaleSize();
            mp.x = mp.x * scaleSize.cx / clientSize.cx;
            mp.y = mp.y * scaleSize.cy / clientSize.cy;
        }
    }
    return mp;
}

FCSize GTKHost::getSize(){
    if (m_native->allowScaleSize()){
        return m_native->getScaleSize();
    }
    else{
        return getClientSize();
    }
}

int GTKHost::getUnionRect(FCRect *lpDestRect, const FCRect *lpSrc1Rect, const FCRect *lpSrc2Rect){
    return 0;
}

void GTKHost::invalidate(){
    m_clipBounds.clear();
    FCRect clipRect = {0, 0, m_native->getSize().cx, m_native->getSize().cy};
    m_clipBounds.add(clipRect);
    if(m_drawingArea){
        gtk_widget_queue_draw(m_drawingArea);
    }
}

void GTKHost::invalidate(const FCRect& rect){
    if(m_allowPartialPaint){
        FCSize displaySize = m_native->getSize();
        double scaleFactorX = 1, scaleFactorY = 1;
        FCSize clientSize = getClientSize();
        if (m_native->allowScaleSize()){
            if (clientSize.cx > 0 && clientSize.cy > 0){
                FCSize scaleSize = m_native->getScaleSize();
                scaleFactorX = (double)(clientSize.cx) / scaleSize.cx;
                scaleFactorY = (double)(clientSize.cy) / scaleSize.cy;
            }
        }
        FCRect newRect = rect;
        if(scaleFactorX > 0 && scaleFactorY > 0){
            newRect.left = floor(newRect.left * scaleFactorX);
            newRect.top = floor(newRect.top * scaleFactorY);
            newRect.right = ceil(newRect.right * scaleFactorX);
            newRect.bottom = ceil(newRect.bottom * scaleFactorY);
        }
        m_clipBounds.add({rect.left >= 0 ? rect.left : 0, rect.top >= 0 ? rect.top : 0, rect.right <= displaySize.cx ? rect
                .right : displaySize.cx, rect.bottom <= displaySize.cy ? rect.bottom : displaySize.cy});
        if(m_drawingArea){
            gtk_widget_queue_draw(m_drawingArea);
        }
    }
    else{
        invalidate();
    }
}

void GTKHost::invoke(FCView *view, Object args){
    int seraialID = 0;
    m_lock.lock();
    m_invokeSerialID++;
    seraialID = m_invokeSerialID;
    m_invokeArgs[seraialID] = args;
    m_invokeViews[seraialID] = view;
    m_lock.unLock();
}

bool GTKHost::isKeyPress(char key){
    return false;
}

void GTKHost::onPaint(const FCRect& rect){
    FCSize displaySize = m_native->getSize();
    double scaleFactorX = 1, scaleFactorY = 1;
    FCSize clientSize = getClientSize();
    if (m_native->allowScaleSize()){
        if (clientSize.cx > 0 && clientSize.cy > 0){
            FCSize scaleSize = m_native->getScaleSize();
            scaleFactorX = (double)(clientSize.cx) / scaleSize.cx;
            scaleFactorY = (double)(clientSize.cy) / scaleSize.cy;
        }
    }
    FCPaint *paint = m_native->getPaint();
    FCRect wRect = {0, 0, clientSize.cx, clientSize.cy};
    paint->setScaleFactor(scaleFactorX, scaleFactorY);
    FCRect clipRect = {0};
    int clipBoundsSize = m_clipBounds.size();
    clipRect = {0};
    if (clipBoundsSize > 0) {
        for (int i = 0; i < clipBoundsSize; i++) {
            FCRect clipBounds = m_clipBounds.get(i);
            if (i == 0) {
                clipRect = {clipBounds.left, clipBounds.top, clipBounds.right, clipBounds.bottom};
            } else {
                if (clipRect.left > clipBounds.left) {
                    clipRect.left = clipBounds.left;
                }
                if (clipRect.right < clipBounds.right) {
                    clipRect.right = clipBounds.right;
                }
                if (clipRect.top > clipBounds.top) {
                    clipRect.top = clipBounds.top;
                }
                if (clipRect.bottom < clipBounds.bottom) {
                    clipRect.bottom = clipBounds.bottom;
                }
            }
        }
    } else {
        clipRect = {0, 0, displaySize.cx, displaySize.cy};
    }
    m_clipBounds.clear();
    if(wRect.right - wRect.left > 0 && wRect.bottom - wRect.top > 0){
        paint->beginPaint(0, wRect, clipRect);
        m_native->onPaint(clipRect);
        paint->endPaint();
    }
}

void GTKHost::onTimer(){
    if(m_native){
        ArrayList<int> timerIDs;
        std::map<int, GTKTimer*>::iterator sIter = m_timers.begin();
        for(; sIter != m_timers.end(); ++sIter){
            GTKTimer *timer = sIter->second;
            if (timer->m_tick % timer->m_interval == 0){
                timerIDs.add(timer->m_timerID);

            }
            timer->m_tick++;
        }
        for(int i = 0; i < timerIDs.size(); i++){
            m_native->onTimer(timerIDs.get(i));
        }
        timerIDs.clear();
        ArrayList<int> sIds;
        m_lock.lock();
        std::map<int, Object>::iterator sIterX = m_invokeArgs.begin();
        for(; sIterX != m_invokeArgs.end(); ++sIterX){
            sIds.add(sIterX->first);
        }
        m_lock.unLock();
        if(sIds.size() > 0) {
            for (int i = 0; i < sIds.size(); i++) {
                Object args = 0;
                FCView *view = 0;
                int invokeSerialID = sIds.get(i);
                m_lock.lock();
                std::map<int, Object>::iterator sIter = m_invokeArgs.find(invokeSerialID);
                if (sIter != m_invokeArgs.end()) {
                    args = sIter->second;
                    m_invokeArgs.erase(sIter);
                }
                std::map<int, FCView *>::iterator sIter2 = m_invokeViews.find(invokeSerialID);
                if (sIter2 != m_invokeViews.end()) {
                    view = sIter2->second;
                    m_invokeViews.erase(sIter2);
                }
                m_lock.unLock();
                if (view) {
                    view->onInvoke(args);
                }
            }
        }
    }
}

lstring GTKHost::paste(){
    return "";
}

void GTKHost::setAllowOperate(bool allowOperate){
    m_allowOperate = allowOperate;
}

void GTKHost::setAllowPartialPaint(bool allowPartialPaint){
    m_allowPartialPaint = allowPartialPaint;
}

void GTKHost::setCursor(FCCursors cursor){
}

void GTKHost::showToolTip(const String& text, const FCPoint& mp){
}

void GTKHost::startTimer(int timerID, int interval){
    interval = interval / 10;
    if(interval < 1){
        interval = 1;
    }
    //m_lock.lock();
    std::map<int, GTKTimer*>::iterator sIter = m_timers.find(timerID);
    if(sIter != m_timers.end()){
        sIter->second->m_interval = interval;
        sIter->second->m_tick = 0;
    }
    else{
        GTKTimer *timer = new GTKTimer;
        timer->m_interval = interval;
        timer->m_timerID = timerID;
        m_timers[timerID] = timer;
    }
}

void GTKHost::stopTimer(int timerID){
    std::map<int, GTKTimer*>::iterator sIter = m_timers.find(timerID);
    if(sIter != m_timers.end()){
        delete sIter->second;
        m_timers.erase(sIter);
    }
}
